package cn.schoolwow.quickdao.module.database.parent.flow.executor.common;

import cn.schoolwow.quickdao.domain.database.parent.DatabaseType;
import cn.schoolwow.quickflow.domain.FlowContext;
import cn.schoolwow.quickflow.flow.BusinessFlow;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.*;
import java.time.*;
import java.util.Collection;
import java.util.Iterator;

public class SetPrepareStatementParameterFlow implements BusinessFlow {
    private Logger logger = LoggerFactory.getLogger(SetPrepareStatementParameterFlow.class);

    @Override
    public void executeBusinessFlow(FlowContext flowContext) throws Exception {
        DatabaseType databaseType = flowContext.checkInstanceData(DatabaseType.class);
        PreparedStatement preparedStatement = (PreparedStatement) flowContext.checkData("preparedStatement");
        Collection parameters = (Collection) flowContext.getData("parameters");
        
        if (null == parameters||parameters.isEmpty()) {
            return;
        }
        int parameterIndex = 1;
        Iterator iterator = parameters.iterator();
        while (iterator.hasNext()) {
            Object parameter = iterator.next();
            if (null == parameter) {
                preparedStatement.setObject(parameterIndex, null);
            } else {
                switch (parameter.getClass().getName()) {
                    case "byte": {
                        preparedStatement.setByte(parameterIndex, (byte) parameter);
                    }
                    break;
                    case "[B": {
                        preparedStatement.setBytes(parameterIndex, (byte[]) parameter);
                    }
                    break;
                    case "boolean": {
                        boolean value = (boolean) parameter;
                        preparedStatement.setBoolean(parameterIndex, value);
                    }
                    break;
                    case "short": {
                        preparedStatement.setShort(parameterIndex, (short) parameter);
                    }
                    break;
                    case "int": {
                        preparedStatement.setInt(parameterIndex, (int) parameter);
                    }
                    break;
                    case "float": {
                        preparedStatement.setFloat(parameterIndex, (float) parameter);
                    }
                    break;
                    case "long": {
                        preparedStatement.setLong(parameterIndex, (long) parameter);
                    }
                    break;
                    case "double": {
                        preparedStatement.setDouble(parameterIndex, (double) parameter);
                    }
                    break;
                    case "java.lang.String": {
                        preparedStatement.setString(parameterIndex, (String) parameter);
                    }
                    break;
                    case "java.util.Date": {
                        java.util.Date date = (java.util.Date) parameter;
                        preparedStatement.setDate(parameterIndex, new Date(date.getTime()));
                    }
                    break;
                    case "java.sql.Date": {
                        Date date = (Date) parameter;
                        preparedStatement.setDate(parameterIndex, date);
                    }
                    break;
                    case "java.sql.Time": {
                        Time time = (Time) parameter;
                        preparedStatement.setTime(parameterIndex, time);
                    }
                    break;
                    case "java.sql.Timestamp": {
                        Timestamp timestamp = (Timestamp) parameter;
                        preparedStatement.setTimestamp(parameterIndex, timestamp);
                    }
                    break;
                    case "java.time.LocalDate": {
                        LocalDate localDate = (LocalDate) parameter;
                        switch (databaseType) {
                            case Oracle: {
                                //oracle不支持直接设置LocalDate类型
                                ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
                                Date date = new Date(Date.from(zonedDateTime.toInstant()).getTime());
                                preparedStatement.setObject(parameterIndex, date);
                            }
                            break;
                            default: {
                                preparedStatement.setObject(parameterIndex, localDate);
                            }
                            break;
                        }
                    }
                    break;
                    case "java.time.LocalDateTime": {
                        LocalDateTime localDateTime = (LocalDateTime) parameter;
                        switch (databaseType) {
                            case Oracle: {
                                //oracle不支持直接设置LocalDateTime类型
                                ZoneId zoneId = ZoneId.systemDefault();
                                Instant instant = localDateTime.atZone(zoneId).toInstant();
                                Timestamp timestamp = new Timestamp(instant.toEpochMilli());
                                preparedStatement.setObject(parameterIndex, timestamp);
                            }
                            break;
                            default: {
                                preparedStatement.setObject(parameterIndex, localDateTime);
                            }
                            break;
                        }
                    }
                    break;
                    case "java.sql.Array": {
                        preparedStatement.setArray(parameterIndex, (Array) parameter);
                    }
                    break;
                    case "java.math.BigDecimal": {
                        preparedStatement.setBigDecimal(parameterIndex, (BigDecimal) parameter);
                    }
                    break;
                    case "java.sql.Blob": {
                        preparedStatement.setBlob(parameterIndex, (Blob) parameter);
                    }
                    break;
                    case "java.sql.Clob": {
                        preparedStatement.setClob(parameterIndex, (Clob) parameter);
                    }
                    break;
                    case "java.sql.NClob": {
                        preparedStatement.setNClob(parameterIndex, (NClob) parameter);
                    }
                    break;
                    case "java.sql.Ref": {
                        preparedStatement.setRef(parameterIndex, (Ref) parameter);
                    }
                    break;
                    case "java.net.URL": {
                        preparedStatement.setURL(parameterIndex, (URL) parameter);
                    }
                    break;
                    case "java.sql.RowId": {
                        preparedStatement.setRowId(parameterIndex, (RowId) parameter);
                    }
                    break;
                    case "java.sql.SQLXML": {
                        preparedStatement.setSQLXML(parameterIndex, (SQLXML) parameter);
                    }
                    break;
                    case "java.io.InputStream": {
                        preparedStatement.setBinaryStream(parameterIndex, (InputStream) parameter);
                    }
                    break;
                    case "java.io.Reader": {
                        preparedStatement.setCharacterStream(parameterIndex, (Reader) parameter);
                    }
                    break;
                    case "com.alibaba.fastjson.JSONObject": {
                        JSONObject o = (JSONObject) parameter;
                        String jsonString = o.toJSONString();
                        preparedStatement.setObject(parameterIndex, jsonString);
                    }
                    break;
                    case "com.alibaba.fastjson.JSONArray": {
                        JSONArray o = (JSONArray) parameter;
                        String jsonString = o.toJSONString();
                        preparedStatement.setObject(parameterIndex, jsonString);
                    }
                    break;
                    default: {
                        try {
                            preparedStatement.setObject(parameterIndex, parameter);
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            logger.trace("设置参数,索引:{},值:{}", parameterIndex, parameter);
            parameterIndex++;
        }
    }

    @Override
    public String name() {
        return "设置SQL语句参数";
    }
}
